#include "StageManager.h"

// Constructores y destructores ----------------------------------------

template<> StageManager* Ogre::Singleton<StageManager>::ms_Singleton = 0;

StageManager::StageManager(btDiscreteDynamicsWorld* pWorld,
                           Ogre::SceneManager* pSceneManager) :
  _pWorld(pWorld), _pSceneManager(pSceneManager) {}

StageManager::~StageManager() {

  _stages.clear();
}

// Ogre::Singleton -----------------------------------------------------

StageManager&
StageManager::getSingleton() {

  assert(ms_Singleton);
  return *ms_Singleton;
}

StageManager*
StageManager::getSingletonPtr() { return ms_Singleton; }

// Factory -------------------------------------------------------------

void
StageManager::createStage(const std::string stageMesh,
                          const std::string collisionMesh) {

  // Crear entidad de la forma de colision
  Ogre::Entity* pCollisionEntity =
    _pSceneManager->createEntity(collisionMesh);

  // Cargar entidad en convertidor btOgre
  BtOgre::StaticMeshToShapeConverter converter(pCollisionEntity);


  // Obtener forma de colisión
  btBvhTriangleMeshShape* pStageShape = converter.createTrimesh();

  // Eliminar entidad de la forma de colision
  _pSceneManager->destroyEntity(pCollisionEntity);

  // Crear entidad del escenario
  Ogre::Entity* pStageEntity = _pSceneManager->createEntity(stageMesh);
  pStageEntity->setQueryFlags(QUERY_MASK_STAGE);
  //pStageEntity->setLightMask(LIGHT_MASK_STAGE);

  // Crear nodo
  Ogre::SceneNode* pStageNode =
    _pSceneManager->getRootSceneNode()->createChildSceneNode();

  pStageNode->attachObject(pStageEntity);

  // Crear MotionState
  MyMotionState* pStageMotionState =
    new MyMotionState(btTransform(btQuaternion(0, 0, 0, 1),
                                  btVector3(0, 0, 0)), pStageNode);

  // Fijar masa y calcular inercia
  btScalar mass = 0;
  btVector3 fallInertia(0, 0, 0);
  pStageShape->calculateLocalInertia(mass, fallInertia);

  // Crear información de construcción
  btRigidBody::btRigidBodyConstructionInfo
    stageBodyCI(mass, pStageMotionState, pStageShape, fallInertia);

  // Crear cuerpo rígido
  btRigidBody* pStageBody = new btRigidBody(stageBodyCI);

  // Añadir cuerpo al mundo
  _pWorld->addRigidBody(pStageBody);

  // Crear Stage
  Stage* pStage = new Stage(_pWorld, pStageShape, pStageBody,
                            _pSceneManager, pStageNode, pStageEntity);

  // Guardar Stage
  _stages.push_back(pStage);
}

// Lights --------------------------------------------------------------

void
StageManager::setSceneShadow() {

  // Sombras

  // Tecnica
  _pSceneManager->setShadowTechnique(Ogre::SHADOWTYPE_STENCIL_MODULATIVE);

  // Color
  _pSceneManager->setShadowColour(Ogre::ColourValue(0.3, 0.3, 0.3));

  // Luz ambiental
  _pSceneManager->setAmbientLight(Ogre::ColourValue(0.3, 0.3, 0.3));
}

void
StageManager::createMoon() {

  Ogre::Light* moon = _pSceneManager->createLight("Moon");

  // Luz direccional
  moon->setType(Ogre::Light::LT_DIRECTIONAL);

  // Dirección
  moon->setDirection(Ogre::Vector3(-22.88,
                                   -35.30,
                                   -54.48));

  // Color difuso
  moon->setDiffuseColour(Ogre::ColourValue(0.06383,
                                           0.08904,
                                           0.220));

  // Color especular
  moon->setSpecularColour(Ogre::ColourValue::White);

  // Mascara de iluminación
  //moon->setLightMask(LIGHT_MASK_CHARACTER);

  // Emitir sombras
  moon->setCastShadows(true);
}

void
StageManager::createStreetlight(const Ogre::Vector3 position) {

  Ogre::Light* streetlight = _pSceneManager->createLight();

  // Luz puntual
  streetlight->setType(Ogre::Light::LT_POINT);

  // Posición
  streetlight->setPosition(position);

  // Color difuso
  streetlight->setDiffuseColour(Ogre::ColourValue(0.889,
                                                  1,
                                                  0.198));

  // Color especular
  streetlight->setSpecularColour(Ogre::ColourValue::White);

  // Emitir sombras
  streetlight->setCastShadows(true);

  // Atenuación
  streetlight->setAttenuation(4, 2, 2, 2);
}
